public class ONEN_OppContactRoles {

	public static boolean haveCheckedContactRoles = false;

	public static Id GetPrimaryContactId (Id oppID){

		Id primaryId;		

		// get the primary contract role record (if any) for this opp
		OpportunityContactRole[] conRoles = [select ContactId From OpportunityContactRole WHERE Opportunity.Id = :oppID AND IsPrimary = true];
		if (conRoles.size()>0) {
			//get the primary Contact's Id
			primaryID = conRoles[0].ContactId;
		}
		
		return primaryId;
	}
	
	public static String GetHouseholdSoftCreditRole (){
		return 'Individual Donor - Household Member';
	}
	
	public static map<Id,Id> GetPrimaryContactIdsBulk (set<id> oppIds) {
		
		// given a list of OppID's (in a set)
		// look up the primary contacts from ConRole (if any)
		// and return a map of OppID->PrimaryConID
		// looks like we can return maps from methods, although the lang reference doesn't specify you can
		
		map <Id,Id> ContactIdMap = new Map <Id,Id> ();
		
		// query for OppConRoles where primary=true and OppID in arg set
		OpportunityContactRole[] conRoles = [select OpportunityId, ContactId From OpportunityContactRole WHERE IsPrimary = true AND Opportunity.Id IN :oppIds ];
		
		// now loop through the results and build the map
		for (OpportunityContactRole thisCR : conRoles) {
			ContactIdMap.put( thisCR.OpportunityId, thisCR.ContactId );
		}
		
		return ContactIdMap;
		
	}
	
	
	public static void CreateOppConRolesBulk ( list<Opportunity> opps , list<id> ConIds ) {
	
	// new bulk-safe version of this by MS
	// given a list of opps with 'dummy' ConId and Role fields populated,
	// create ConRole records
	
	// args now expect both a map from OppId to ConId,
	// and then also a list of those same Opps as objects
	
	// moved from original spot in class GivingHistory
		
		
		system.debug ('class method called for ' + opps.size() + ' Opps');
		
		list<OpportunityContactRole> OCRtoCreate = new list<OpportunityContactRole> ();
		
		// first ensure that the ID stored in the opp is in fact a valid con id
			// if any of them aren't, we'll just skip 'em
		map<id, Contact> validCons = new Map<id, Contact> ([SELECT id, FirstName,ONEN_Household__c FROM Contact WHERE id IN :ConIds ]);
		//get a set of all the households for primary contact roles in this batch of opps
		set<id> allHouseholds = new set<id>();
		//loop through the primary contacts, adding the households to the set
		for (Contact primaryContact : validCons.values()) {
			allHouseholds.add(primaryContact.ONEN_Household__c);
		}
		//get full Contacts for the households
		map<id, Contact> validHouseholdCons = new Map<id, Contact> ([SELECT id, ONEN_Household__c FROM Contact WHERE ONEN_Household__c in :allHouseholds ]);
		//a new map to hold all the Contacts for each Households
		map<id,List<Contact>> contactsInHousehold = new Map<id,List<Contact>> ();	
		//looping through the contacts, creating/adding the hh to contact map records as necessary	
		for (Contact possibleHouseholdMember : validHouseholdCons.values()) {
			List<Contact> contactsForHousehold = new List<Contact>();
			//if the household is already represented, pull the existing list
			if (contactsInHousehold.get(possibleHouseholdMember.ONEN_Household__c)!=null) {
				contactsForHousehold = contactsInHousehold.get(possibleHouseholdMember.ONEN_Household__c);
			}
			//add this contact to the list
			contactsForHousehold.add(possibleHouseholdMember);
			//put the list in the map
			contactsInHousehold.put(possibleHouseholdMember.ONEN_Household__c,contactsForHousehold);
							
		}
		
		// if so, create ConRoles
		// (by looping through original opps and seeing if we have a matching valid contact)
		
		// TBD we may eventually want to double check that none of these already have contact roles
		// 		because users may import or enter a conid for an opp that duplicates an existing role
		//		(this would only be for a trigger other than CreateContactRoles, which only fires after insert, not after update

		for ( Opportunity thisOpp : opps ) {
			//get the primary contact
			Contact primaryContact = validCons.get(thisOpp.CR_Contact_Id__c);
			//if we have a primary contact
			if (primaryContact != null ) {
				
				//create a role for the primary contact
				OpportunityContactRole newOCR = new OpportunityContactRole (
					OpportunityId = thisOpp.id,
					ContactId = primaryContact.Id,
					Role = thisOpp.CR_Contact_Role__c,
					IsPrimary = true
				);
				
				OCRtoCreate.add (newOCR);
				//get the primary contact's household
				Id thisPrimaryContactHouseholdId = primaryContact.ONEN_Household__c;
				if(primaryContact.ONEN_Household__c!=null&&thisOpp.CR_HHroles__c){
					List<Contact> contactsForThisOpp = new List<Contact>();
					contactsForThisOpp = contactsInHousehold.get(thisPrimaryContactHouseholdId);
					//if there is more than just the primary contact in the list
					if (contactsForThisOpp.size()>1) {
						//create a role for each contact that isn't the primary						
						for (Contact c : contactsForThisOpp) {
							if (c.Id!=thisOpp.CR_Contact_Id__c) {
								OpportunityContactRole HHSoftCreditRole = new OpportunityContactRole(
									OpportunityId=thisOpp.id,
									ContactId=c.id,
									Role=GetHouseholdSoftCreditRole()
								);
								OCRtoCreate.add (HHSoftCreditRole);
							}
						}
					}
					//system.debug ('ConRoles to create: ' + OCRtoCreate.size() );
					
				}				
			}
		}
	//	system.debug ('ConRoles to create: ' + OCRtoCreate);
		insert OCRtoCreate;
		
		
		// for now we're just adding the primary role, might want to later insert roles
		// for the other HH members as well (or at least give an option via the args
		
	}
	

	public static void CheckContactRoles (map<id,Opportunity> triggermap) {

		// this is the logic that usually goes in the trigger
		// factored out to here so that this can be called by any of the other
		// Opp AfterInsert triggers
		// to ensure that opp creation happens first (see trigger CreateContactRoles)

		// pass in the trigger.newmap from the calling trigger
		
		system.debug ('CheckContactRoles: CALLED');
		
		list<Opportunity> OppsToProcess = new list<Opportunity> ();
		list<id> ConIds = new list<id> ();
		
		// collect those Opps with the CR Contact ID dummy field set
		for ( Opportunity thisOpp : triggermap.values() ) {
			if ( thisOpp.CR_Contact_Id__c != null ) {
				ConIds.add (thisOpp.CR_Contact_Id__c );
				OppsToProcess.add (thisOpp);
			}
		}
		
		system.debug ('CheckContactRoles opps to process: ' + OppsToProcess.size() );
		
		// update those Opps
		if ( OppsToProcess.size() > 0 ) {
			CreateOppConRolesBulk ( OppsToProcess , ConIds);
		}
		
		// and record the fact that we've done this (so it won't be done again)
		haveCheckedContactRoles = true;
		
	}

	
/*	public static void CheckContactRoles_Manual (list<Opportunity> opps) {

		// this may seem a little weird, but this method is mostly here to allow 
		// testmethods to manually make sure that contact roles get created
		// the problem being that since a single test method may do more than one Opp insert
		// the 2nd one and so on won't naturally trigger conrole creation
		// since the haveChecked var will be true by then
		// this should never happen in the wild since each opp insert is a separate namespace
		
		map <id,Opportunity> oppMap = new map<id,Opportunity> ();
		
		for ( Opportunity thisOpp : opps ) {
			oppMap.put (thisOpp.id,thisOpp);
			CheckContactRoles (oppMap);
		}
	
	}
*/

}